#include <stdio.h>
#include <poll.h>
#include "exec.h"

InsnType
get_insn_type (unsigned short *insn) {
    InsnType type;
    DEF_INST (ADD, *insn, 0xF038, 0x1000, {
        type = INSN_ADD;
    }); 
    DEF_INST (ADD,*insn, 0xF020, 0x1020, {
        type = INSN_ADDI;
    });
    DEF_INST (AND, *insn,0xF038, 0x5000, {
        type = INSN_AND;
    });
    DEF_INST (AND,*insn, 0xF020, 0x5020, {
        type = INSN_ANDI;
    });
    DEF_INST (BR, *insn,0xF000, 0x0000, {
        type = INSN_BR;
    });
    DEF_INST (JMP, *insn,0xFE3F, 0xC000, {
        type = INSN_JMP;
    });
    DEF_INST (JSR, *insn,0xF800, 0x4800, {
         type = INSN_JSR;
    });
    DEF_INST (JSRR, *insn,0xFE3F, 0x4000, {
        type = INSN_JSRR;
    });
    DEF_INST (LD, *insn,0xF000, 0x2000, {
        type = INSN_LD;
    });
    DEF_INST (LDI,*insn, 0xF000, 0xA000, {
        type = INSN_LDI;
    });
    DEF_INST (LDR,*insn, 0xF000, 0x6000, {
        type = INSN_LDR;
    });
    DEF_INST (LEA,*insn, 0xF000, 0xE000, {
        type = INSN_LEA;
    });
    DEF_INST (NOT, *insn,0xF03F, 0x903F, {
        type = INSN_NOT;
    }); 
    DEF_INST (RTI,*insn, 0xFFFF, 0x8000, {
        type = INSN_RTI;
    });
    DEF_INST (ST,*insn, 0xF000, 0x3000, {
        type = INSN_ST;
    });
    DEF_INST (STI,*insn, 0xF000, 0xB000, {
        type = INSN_STI;
    });
    DEF_INST (STR,*insn, 0xF000, 0x7000, {
        type = INSN_STR;
    });
    DEF_INST (TRAP,*insn, 0xFF00, 0xF000, {
        type = INSN_TRAP;
    });
    executed:
    return type;
}

int recv_char = 0;

target_size_t
read_memory (MachineStatus *ms, target_size_t addr, target_size_t* dest) {
    struct pollfd p;
    if (addr == 0xFE00) { /* KBSR */
        p.fd = fileno (stdin);
        p.events = POLLIN;
        if (poll (&p, 1, -1) == 1 && (p.revents & POLLIN) != 0)
            recv_char = 1;
        else
            recv_char = 0;
        (*dest) = (recv_char ? 0x8000 : 0x0000);
    } else if (addr == 0xFE02) {
        char c;
        if (recv_char == 1) {
            c = getchar ();
            ms->memory[addr] = c;
            (*dest) = ms->memory[addr];
            recv_char = 0;
        }
    } else if (addr == 0xFE04) { /* DSR */
        (*dest) = 0x8000;
    }  else if (addr == 0xFE06) { /* DDR */
        (*dest) = 0x0000;
    } else {
        (*dest) = ms->memory[addr];
    }
    return  (*dest);
}

target_size_t
write_memory (MachineStatus *ms, target_size_t addr, target_size_t value) {
    if (addr == 0xFE06) {
        printf ("%c", value);
        fflush (stdout);
    } else if (addr == 0xFFFE) {
        if (value & 0x8000 != 0) {
            ms->hlt = TRUE;
        }
    } else {
        ms->memory[addr] = value;
    }
    return value;
}